Objekterkennung von Plastik in Flüssen

Projekt in Programming Languages For Data Science Annika Scheug, Sommersemester 2023

Use Case Beschreibung

Unsere Ozeane sind zunehmend von Plastikmüll verschmutzt. Vieles davon gelangt vom Inland über Flüsse ins Meer. Daher muss der Plastikmüll aus den Flüssen gefischt werden, bevor er ins Meer gelangt. Diese Arbeit kann mithilfe von Objekterkennung erleichtert werden.
Daher soll ein Modell zur Erkennung von Plastikmüll in Flüssen zu trainiert werden. Dafür wird ein Datensatz mit über 3000 Trainingsdaten verwendet (https://kili-technology.com/data-labeling/machine-learning/kili-s-community-challenge-plastic-in-river-dataset). Ziel ist es, dass das Modell verschiedene Arten von Plastikmüll (Plastikflasche, -tüte oder andere) zuverlässig erkennt und somit der Müll gezielt entfernt werden kann.
Echte Bildaufnahmen könnten später durch das Anbringen von Kameras an geeigneten Stellen gewonnen werden. Diese können an das Modell übergeben werden und dieses identifiziert wo auf dem Bild sich welche Art von Plastikmüll befindet.
Relevante Variablen zu einem Bild sind somit das Label des Plastikmüllobjekts sowie dessen Location auf dem Bild. Diese Angabe wird mithilfe sogenannter Bounding Boxes vorgenommen.

Das Projekt beinhaltet die prototypische Implementierung des Object Detection Modells und dessen Deployment in einer Shinyapp. Die Modellierung erfolgt in diesem Jupyter Notebook, während die Shinyapp in RStudio entwickelt wird.

Für die Modellierung wird Huggingface Transformers genutzt. Der Datensatz ist dort bereits als Huggingface Dataset vorhanden und kann direkt geladen werden. Zudem wird ein vortrainiertes Modell für Object Detection geladen und über die Trainer API für den spezifischen Anwendungsfall optimiert.
Für die Evaluierung wird zunächst der Loss betrachtet. Als weitere Metrik werden Mean Average Precision und Mean Average Recall unter Verwendung der Intersection over Union (IoU) berechnet. Diese gibt an, inwieweit sich die tatsächliche Bounding Box mit der durch das Modell vorhergesagten Bouding Box überschneiden. Die IoU kann Werte zwischen 0 und 1 einnehmen, wobei ein höherer Wert besser ist. Für die Bestimmung von Precision und Recall wird ein Threshold für IoU bestimmt, ab welchem eine Vorhersage als Positive gewertet wird. Somit können True Positive, False Positive etc. bestimmt werden.
Im Anschluss an die Metriken werden für eine abschließende Evaluation einige Beispiele mit ihren vorhergesagten und tatsächlichen Bounding Boxes geplottet und miteinander verglichen.

Datenimport und -exploration

Zunächst werden notwendige Bibliotheken installiert und importiert und anschließend die Daten von Huggingface geladen.

Das Datenset ist bereits in Trainings-, Validierungs- und Testdaten gesplittet.
Es enthält 3407 Trainingsdaten, 425 Validierungsdaten und 427 Testdaten. Diese enthalten jeweils ein Bild und Label.

Ein einzelnes Datenelement enthält ein Bild mit Größenangabe, sowie ein Dictionary mit Labeldaten. Dazu gehören die Label der Objekte auf dem Bilder als Array, sowie deren Bounding Boxes als weiterer Array je Box.
Die Bounding Boxes sind normalisiert (zu erkennen an den Werten zwischen 0 und 1) und müssen daher für einen Plot mit der Imagesize multipliziert werden.
Ein Datensatz enthält aktuell keine eindeutige image_id. Außerdem fehlt die Angabe der Box Area, welche später für die Modellierung benötigt wird. Ein einzelnes Bild kann über über den Zusatz ['image'] angezeigt werden.
Das oben gezeigte Beispiel mit Größe 1280x740 enthält zwei Label, was bedeutet, dass zwei Plastikobjekte auf dem Bild gelabelt wurden. Dieses wird nachfolgend angezeigt.

In der oberen Bildmitte sind die zwei Objekte zu erkennen.

Die Untersuchung eines weiteren Beispiels zeigt, dass nicht alle Bilder die gleiche Größe haben. Das untere Beispiel hat eine Größe von 1280x640 Pixeln.

Als nächstes wird der Rohdatensatz umgewandelt. Dafür werden weitere Spalten image_id und area hinzugefügt. Die Box Area kann dabei aus den Werten der Bounding Boxes bestimmt werden. Diese müssen zuvor mit der Bildgröße multipliziert und auf das richtige Format xyxy transformiert werden. In den Rohdaten liegen die Bouding Boxes im Format cxcywh vor, also Angabe der normalisierten Pixelwerte von der Center X und Y Koordinate, sowie Breiten (width w) nd Höhenangabe (height h) der Bounding Box.

Mithilfe der map() Methode von huggingface datasets werden die neu erzeugten Spalten jedem Datenelement angehängt.

Das Datenset hat nun die benötigte Struktur. Es wird daher gespeichert, sodass bei einer Wiederausführung des Skripts die Daten nicht immer wieder neu aus Huggingface geladen werden müssen. Stattdessen kann direkt das bearbeitet Datenset aus dem lokalen Speicher geladen werden.

Als nächstes wird untersucht, welche verschiedenen Ausprägungen von Plastikmüll im Datenset vorliegen.

Es existieren vier Arten von Objekten im Datensatz: Plastiktüte, Plastikflasche, anderer Plastikmüll und kein Plastikmüll.
Für die Modellierung werden nachfolgend Mappings von ID zu Label und umgekehrt erstellt.

Als nächstes sollen die Label der Daten genauer untersucht werden. Dafür werden einige Beispielbilder zuerst ohne und dann mit Bounding Boxes und Labeln geplotted.
Hierfür wird die Funktion draw_bb erstellt.

Die Plastikflasche im Bild ist korrekt gelabelt.

Im Bild sind einige Plastikflaschen im Fluss zu erkennen. Allerdings sind nur zwei von ihnen gelabelt, wie unten nochmal am Datensatz zu erkennen. Eine gewisse Unschärfe der Trainingsdaten ist daher vorhanden. Dies könnte sich negativ auf das Modelltraining auswirken.

Dieses Beispiel ist sehr gut gelabelt.
Insgesamt ist bereits an den wenigen Beispielen auffällig, dass es wesentlich mehr Plastikflaschen als anderen Plastikmüll auf den Bildern im Datensatz zu geben scheint. Es ist daher zu erwarten, dass das Modell hauptsächlich Plastikflaschen entdecken wird aufgrund von Biased Data.

Preprocessing

Im nächsten Schritt werden die Daten für die Modellierung vorbereitet.
Huggingface Models können bereits mit einem eigenen image_processor heruntergeladen werden. Allerdings benötigt auch dieser ein spezielles Inputformat, sodass die Daten vorher noch weiter bearbeitet werden müssen.
Als Model wurde das facebook/detr-resnet-50 Object Detection zum Finetuning ausgewählt (https://huggingface.co/facebook/detr-resnet-50), da sich dieses großer Beliebtheit bedient und gut performt. Es wurde bereits über 300 epochs auf 16 V100 GPUs trainiert und kann zwischen 100 Objektklassen unterscheiden.
Daher wird nun der image_processor dieses Modells geladen.

Mithilfe von Image Augmentation kann die Datenmenge des Trainingssets künstlich vergrößert werden und Overfitting des Models verhindert werden.. Jedes Bild wird bei seinem Aufruf um einen Zufallsfaktor gespiegelt oder gedreht, sodass es für das Modell wie ein neuer Datensatz wirkt. Dafür wird die Bibliothek "Albumentations" verwendet. Diese sorgt dafür, dass bei einem zufälligen Drehen oder Spiegeln der Bilder die Bounding Boxes entsprechend mitverändert werden, sodass Bild und Label weiterhin zusammenpassen.
Außerdem kann in diesem Schritt die Bildgröße angepasst werden. Die Bilder werden nun alle auf eine einheitliche Größe von 720x720 angepasst. Diese Größe wurde gewählt, da die Bilder aufgrund kleiner Objekte nicht zu klein sein dürfen. Bei den meisten Bildern ist die schmalste Seite 720 Pixel groß. Daher wurde diese Größe für alle Bilder ausgewählt.
Die Test- und Validierungsdaten sollen nicht augmentiert werden. Daher wird eine eigene Methode für diese Daten geschrieben, welche nur die Bildgröße anpasst.

Mithilfe der folgenden Methode wird die Image Augmentation auf einen ausgewählten Datenumfang angewendet, sodass das Ergebnis überprüft werden kann.

Testweise werden 10 Datensätze der Trainingsdaten augmentiert.

Bei jedem Aufruf wird das Bild neu um einen zufälligen Faktor gedreht oder gespiegelt. Die Bounding Boxes verschieben sich entsprechend mit. Außerdem wurde die Bildgröße wie gewünscht angepasst.

Nun wird eine Methode definiert, welche die Daten in das erwartete Format für den image_processor vorbereitet.

Für das Preprocessing der Daten wird nun die zuvor gezeigte Image Augmentation ausgeführt und deren Output mithilfe der eben definierten Methode in das richtige Formate für den image_processor umgewandelt und anschließend an diesen übergeben.
Für Validierungsdaten wird die transform Methode ohne Augmentation aufgerufen.

Nachfolgend wird das Preprocessing auf die Daten angewendet und diese als Train bzw. Validation Data gespeichert.

Die Daten wurden nun in ein Dictionary aus Tensor Objekten transformiert. Dazu gehören pixel_values, pixel_mask, sowie die labels (zusammengesetzt aus image_id, class_labels, boxes, area,..) Dieses Format wird vom Modell genau so erwartet.
Das Preprocessing der Daten für die Modellierung ist nun fast abgeschlossen. Es fehlt noch eine Methode, welche mehrere Bilder zu einem Batch zusammenfassen kann. Dafür dient die nachfolgende Methode, welche im Training je nach Batchsize aufgerufen wird.

Es wurde festgestellt, dass die Trainings- und Validierungsdaten korrupte Daten enthalten. Bei diesen ragt die Bounding Box aus dem Bild heraus, weshalb das Modell im Training nicht damit umgehen kann und abbricht. Aus diesem Grund werden die korrupten Daten aus dem Datenset entfernt.
Mithilfe der auskommentierten Schleifen wurden die korrputen Daten identifiziert. Um Rechenpower zu sparen, wurden die Schleifen nur einmal ausgeführt und die Indizes in der nächsten Codezelle für den nächsten Durchlauf hartkodiert.

Modellierung

Nach der Datenexploration und -transformation folgt nun die Modellierung.
Hierfür wird, wie bereits erwähnt, ein vortrainiertes Modell von Huggingface geladen. Dieses Modell ist auf 100 Objektklassen optimiert, weshalb der Classifier komplett neu trainiert werden muss. Auch die Inputgröße der Daten unterscheidet sich vom ursprünglichen Modell und muss neu initialisiert werden. Alle anderen Gewichte werden übernommen und im Finetuning angepasst.
Bei der Initialisierung des Modells werden die anfangs definierten Mappings von Label zu ID und umgekehrt, sowie die Anzahl der Objekte (num_labels) mitgegeben.

In den Training Arguments werden Hyperparameter (wie bspw. Learning Rate und Weight Decay) definiert. Außerdem wird die Batchgröße festgelegt sowie die Anzahl zu trainierender Epochen. Alle 250 Steps soll zudem eine Evaluierung mit den Validierungsdaten durchgeführt werden. Nach Abschluss des Trainings soll das beste Modell geladen werden.
An dieser Stelle wurden bereits verschiedene Hyperparameter getestet. Dabei kam heraus, dass mind. 10 Epochen notwendig sind, um einigermaßen valide Ergebnisse zu erzielen. Daher wurden in diesem Beispiel 20 Epochen trainiert, um das bestmögliche Ergebnis zu erzielen. An der grundlegenden Modellarchitektur wurde nichts verändert.

Als letzter Vorbereitungsschritt wird ein Trainer Objekt der Huggingface Trainer API initialisiert. Dieses benötigt die Angabe des zu optimierenden Models, die Training Arguments mit den Hyperparametern, die Batch-Funktion collate_fn, sowie die Angabe der Trainings- und Validierungsdaten und des Preprocessors.

Anschließend wird das Training gestartet.

Sowohl Trainings als auch Validation Loss haben sich zu Beginn stark reduziert und ca. ab Step 2250 im selben Wertespektrum eingependelt. Auffällig ist, dass der Trainings Loss stets höher als der Validation Loss ist. Dies könnte an Dropout Layern sowie Image Augmentation in der Traininsphase liegen.
Allerdings erscheint der Loss prinzipiell noch etwas hoch.

Aufgrund langer Trainingszeit wird das Modell direkt im Anschluss an das Training gespeichert, um einen Verlust zu vermeiden. Mit den nachfolgenden Methoden können bereits trainierte und abgespeicherte Modelle geladen werden.

Evaluation

Im letzten Schritt wird das Modell anhand der in der Einleitung beschriebenen Metriken evaluiert.
Zur Bestimmung von Mean Average Precision und Mean Average Recall wird mithilfe von torchvision bestimmt. Hierfür muss das Testdatensatz zunächst in ein bestimmtes Format umgewandelt werden.

Dann wird eine Instanz von CocoDetection erstellt, welche in der cocoevaluation verwendet wird.

Anschließend werden die Metriken berechnet.

Leider erreichen Mean Average Precision und Mean Average Recall selten einen Wert über null. Dies deutet auf ein sehr schlechte Modell hin. Auch andere getestete Modelle konnten keine besseren Werte erzielen.

Im Folgenden werden beispielhaft einige Vorhersagen des Modells genauer betrachtet. Dafür werden zunächst Vorhersagen für Testbilder gemacht und diese anschließend geplottet und mit den Originallabeln verglichen.

Das Modell hat im Beispielbild vier Plastikflaschen erkannt. An drei vorhergesagten Bounding Boxes ist auffällig, dass diese nah beiandner liegen zu scheinen.
Nachfolgenden wird das Bild mit seinem tatsächlichen Label geplottet.

In den Originaldaten hat das Bild ein Label (Plastikflasche). Tatsächlich scheint hierbei eine weitere kleine Flasche knapp hinter der gelabelten übersehen worden zu sein. Rechts vor der gelabelten Flasche schwimmt zudem etwas unerkenntliches im Wasser. Hierbei könnte es sich auch um Plastikmüll handeln, der nicht gelabelt wurde. Nachfolgend wird das Bild mit seinen vorhergesagten Labels geplottet.

Das Modell hat das nicht identifizierbare Objekt im Vordergrund als Flasche erkannt. Die drei ähnlichen Bounding Boxes der Modellvorhersage liegen fast übereinander und sind im Plot kaum zu unterscheiden. Es hat das Objekt daher mehrmals als Flasche erkannt. Zudem wurde ein Blatt im Vordergrund fälschlicherweise als Flasche gelabelt.
Leider wurde die im Datensatz gelabelte rote Flasche nicht erkannt. Allerdings sind die Objekte auf dem Foto auch nur sein klein, selbst für einen Menschen nicht ganz eindeutig zu sehen und daher auch für ein Modell schwer zu erkennen.

Es wird ein weiteres Beispiel genauer untersucht.

In diesem Beispiel hat das Modell die vordere Flasche gut erkannt. Jedoch wurde sie wieder mehrmals (dreimal) gelabelt. Die zweite Flasche im Hintergrund wurde richtig erkannt.

In diesem Beispiel wurden alle Flaschen korrekt vom Modell erkannt.

Finale Bewertung und Ausblick

Die Modellmetriken (Mean Average Precision und Recall) des Modells sind leider sehr schlecht. Allerdings fällt bei genauerer Untersuchung der Modellvorhersagen und ihrem manuellen Vergleich mit den tatsächlichen Lables auf, dass es doch nicht so schlecht ist, wie es auf den ersten Blick scheint.
Das Modell erkennt den Plastikmüll, welcher nicht zu klein dargestellt ist. Es ist sich häufig jedoch nicht genau über die genaue Location der Bounding Boxes sicher und predicted daher öfter mehrere Bounding Boxes pro Objekt. Dies könnte auch mit der Spiegelung der Objekte in der Wasseroberfläche zusammenhängen, wie vor allem an test_im2 auffällig. Zudem wurde festgestellt, dass die Labels in den Trainingsdaten (ground_truth labels) auch nicht immer hundertprozent korrekt sind. Dies wirkt sich natürlich auch negativ auf das Modelltraining und somit dessen Vorhersagen aus.
Ziel des Use Cases ist es, Plastikmüll in Flüssen zu erkennen, sodass dieser entfernt werden kann, bevor er ins Meer gelangt. Für den Use Case ist es daher nicht so schlimm, wenn Objekte mehrmals gelabelt werden. Dadurch wird der Müll trotzdem erkannt und kann beseitigt werden. Schlimmer ist es, wenn Objekte gar nicht erkannt werden, da sie so unentdeckt ins Meer schwimmen können.

Für einen produktiven Einsatz des Modells reicht die Performance dennoch nicht aus. Um diese weiter zu verbessern, wäre es sinnvoll, die Label des Trainingsdatensets nochmal genau zu überprüfen und ggfs. zu korrigieren. Außerdem wurden in diesem Notebook nur Bilder mit Label "Plastikflasche" erkannt. Eine Überprüfung der Objektkategorien in den Trainingsdaten erscheint daher ebenfalls sinnvoll. Gggfs. können nicht benötigte Kategorien entfernt werden.
Darüber hinaus können im Modell Training weitere Hyperparameter ausprobiert und evaluiert werden. Ebenfalls interessant wäre es, ein anderes vortrainiertes Modell (bspw. hustvl/yolos-tiny) für den Anwendungsfall zu optimieren und dessen Performance mit dem facebook/detr-resnet-50 zu vergleichen.

Trotz allem wird im Rahmen dieser Projektarbeit das hierin optimierte Modell in einer R Shinyapp deployed. Dies erfolgt außerhalb dieses Jupyter Notebooks in der Programmiersprache R.